﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.Contracts;

namespace ReactiveLang.Collections
{
    [ContractClass(typeof(IEnumerableExpressionParentContract<>))]
    public interface IEnumerableExpressionParent<in T> : IExpressionParent<Unit>
    {
        IDisposable OnItem(IExpression<T> item, int index);
    }

    #region Contract for IEnumerableExpressionParent<T>

    [ContractClassFor(typeof(IEnumerableExpressionParent<>))]
    abstract class IEnumerableExpressionParentContract<T> : IEnumerableExpressionParent<T>
    {
        public void OnValue(Unit value)
        {
        }

        public void OnException(Exception exception)
        {
        }

        public IDisposable OnItem(IExpression<T> item, int index)
        {
            Contract.Requires<ArgumentNullException>(item != null, "item");
            throw new NotSupportedException();
        }
    }

    #endregion

    public static partial class EnumerableExpressionParent
    {
        class AnonymousParent<T> : IEnumerableExpressionParent<T>
        {
            internal Action onCleared;
            internal Func<IExpression<T>, int, IDisposable> _onItem;
            internal Action<Exception> _onException;

            public void OnValue(Unit value)
            {
                onCleared();
            }

            public void OnException(Exception error)
            {
                _onException(error);
            }

            public IDisposable OnItem(IExpression<T> item, int index)
            {
                return _onItem(item, index);
            }
        }

        public static IEnumerableExpressionParent<T> Create<T>(Func<IExpression<T>, int, IDisposable> onItem, Action onCleared, Action<Exception> onException)
        {
            Contract.Requires<ArgumentNullException>(onItem != null, "onItem");
            Contract.Requires<ArgumentNullException>(onCleared != null, "onCleared");
            Contract.Requires<ArgumentNullException>(onException != null, "onException");
            return new AnonymousParent<T>
            {
                onCleared = onCleared,
                _onItem = onItem,
                _onException = onException
            };
        }
        public static IEnumerableExpressionParent<T> Create<T>(Func<IExpression<T>, int, IDisposable> onItem, Action onCleared)
        {
            Contract.Requires<ArgumentNullException>(onItem != null, "onItem");
            Contract.Requires<ArgumentNullException>(onCleared != null, "onCleared");
            return Create<T>(onItem, onCleared, ex => { throw ex; });
        }

        public static IEnumerableExpressionParent<T> Empty<T>()
        {
            return Util.GenericStorage<T>.EnumerableParent;
        }

        internal static void OnCleared<T>(this IEnumerableExpressionParent<T> parent)
        {
            Contract.Requires<ArgumentNullException>(parent != null, "parent");
            parent.OnValue(default(Unit));
        }
        internal static IDisposable OnItem<T>(this IEnumerableExpressionParent<T> parent, IExpression<T> item)
        {
            Contract.Requires<ArgumentNullException>(parent != null, "parent");
            Contract.Requires<ArgumentNullException>(item != null, "item");
            return parent.OnItem(item, -1);
        }
    }
}
